package com.free.bsf.metric.controller;

import com.free.bsf.core.base.BsfException;
import com.free.bsf.core.base.Ref;
import com.free.bsf.core.db.DbHelper;
import com.free.bsf.core.util.*;
import com.free.bsf.metric.base.Cache;
import com.free.bsf.metric.base.Config;
import com.free.bsf.metric.dao.model.auto.tb_front_metric_model;
import com.free.bsf.metric.dao.model.auto.tb_project_model;
import com.free.bsf.metric.dao.model.auto.tb_sys_config_model;
import com.free.bsf.metric.dao.tb_project_dal;
import com.free.bsf.metric.dao.tb_front_metric_dal;
import com.free.bsf.metric.dao.tb_sys_config_dal;
import lombok.val;
import lombok.var;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;

public class ScriptDownLoadFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        val contextPath = org.springframework.util.StringUtils.trimTrailingCharacter(req.getContextPath(), '/');
        val uri = req.getRequestURI();
        String js = null;
        if (uri.startsWith(contextPath + "/metric.js")) {
            js = "";
            val front_metric_model_cache = Config.LocalCache.get(Config.LocalCacheTime, "front_metric_model", () -> {
                return Optional.ofNullable(DbHelper.get(Config.mysqlDataSource(), (c) -> {
                    return new tb_front_metric_dal().getUse(c);
                }));
            });

            if (front_metric_model_cache.isPresent()) {
                val front_metric_model = (tb_front_metric_model) front_metric_model_cache.get();
                js = front_metric_model.main_script + "\r\n" + front_metric_model.tool_script;
            }
        }
        if (js != null) {
            if (StringUtils.isEmpty(js)) {
                res.setStatus(404);
                return;
            }
            val expireTime = getExpireTime();
            res.setHeader("Content-Type", "application/javascript");
            val js2=js;byte[] data;
            if(isGzip(req)){
                res.setHeader("Content-Encoding", "gzip");
                data = Config.LocalCache.get(Config.LocalCacheTime, "gzip-js-"+js2.hashCode(), () -> {
                    var d = getFileStream(js2);
                    return gzip(d);
                });
            }else{
                data = Config.LocalCache.get(Config.LocalCacheTime, "no-gzip-js-"+js2.hashCode(), () -> {
                    var d = getFileStream(js2);
                    return d;
                });
            }
            setExpiresHeader(res, expireTime);
            writeStream(res, data);
        }
        else{
            chain.doFilter(request, response);
        }
    }

    private Integer getExpireTime() {
        int JsExpireTime = 60;//一分钟
        val cacheExpire_cache = Config.LocalCache.get(Config.LocalCacheTime, "cacheExpire", () -> {
            return Optional.ofNullable(DbHelper.get(Config.mysqlDataSource(), (c) -> {
                return new tb_sys_config_dal().get(c, "JsExpireTime");
            }));
        });
        if (cacheExpire_cache.isPresent()) {
            val cacheExpire = (tb_sys_config_model) cacheExpire_cache.get();
            if (cacheExpire != null) {
                val time = ConvertUtils.tryConvert(cacheExpire.getM_value(), Integer.class);
                if (time != null) {
                    JsExpireTime = time;
                }
            }
        }

        return JsExpireTime;
    }

    private boolean isGzip(HttpServletRequest req){
        return StringUtils.nullToEmpty(req.getHeader("Accept-Encoding")).toLowerCase().contains("gzip");
    }

    private byte[] gzip(byte[] data)  {
        try {
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(data.length)) {
                try (GZIPOutputStream gZIPOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
                    gZIPOutputStream.write(data);
                    gZIPOutputStream.close();
                    byteArrayOutputStream.close();
                    return byteArrayOutputStream.toByteArray();
                }
            }
        }catch (Exception e){
            throw new BsfException(e);
        }
    }

    private void setExpiresHeader(HttpServletResponse rep, int expireTime) {
        rep.setHeader("Cache-Control", "max-age=" + expireTime);
        rep.setHeader("Expires", new Date(System.currentTimeMillis() + expireTime * 1000).toGMTString());
    }

    private void writeStream(HttpServletResponse rep, byte[] data) throws IOException {
        //rep.setHeader("Content-Type", "application/javascript");
        try (OutputStream out = rep.getOutputStream()) {
            out.write(data);
        }
    }

    private byte[] getFileStream(String text) {
        return text.getBytes();
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig filterConfig) {

    }
}
